/*
This file is part of Keepass2Android, Copyright 2013 Philipp Crocoll. This file is based on Keepassdroid, Copyright Brian Pellin.

  Keepass2Android is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Keepass2Android is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Keepass2Android.  If not, see <http://www.gnu.org/licenses/>.
  */

using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Widget;
using Android.Provider;
using Android.Views.Autofill;
using Java.IO;
using KeePass.DataExchange;
using KeePassLib.Cryptography.Cipher;
using KeePassLib.Keys;
using KeePassLib.Serialization;
using KeePassLib.Utility;
using keepass2android.Io;
using keepass2android.Utils;
using KeePassLib;
using KeePassLib.Cryptography.KeyDerivation;
using KeePassLib.Interfaces;
using System.Collections.Generic;
using AndroidX.Preference;
using keepass2android;


namespace keepass2android
{

  /// <summary>
  /// <para>
  /// A helper class that manages language preference display and selection.
  /// </para>
  /// <para>
  /// The idea is to provide a ListPreference with a "System language" item at the top, followed by
  /// the localized list of supported language names. The items are backed by their corresponding "code".
  /// For a langauge that's the 2-char lowercase language code, which is exactly the same code that
  /// LocaleManager.Language expects.
  /// </para>
  /// <para>
  /// "System language" is a special case. LocaleManager.Language expects null, but ListPreference
  /// does not support null as a valid code. To work around this, LanguageEntry.SYS_LANG_CODE
  /// is used as the preference code. LanguageEntry.PrefCodeToLanguage(string) is used to convert the
  /// preference codes to language codes as needed.
  /// </para>
  /// </summary>
  internal class AppLanguageManager
  {
    private readonly PreferenceFragmentCompat _fragment;
    private readonly AndroidX.Preference.ListPreference _langPref;
    private readonly Dictionary<string, LanguageEntry> _langEntriesByCodeUnique;

    public AppLanguageManager(PreferenceFragmentCompat fragment, AndroidX.Preference.ListPreference langPref, HashSet<string> supportedLocales)
    {
      this._fragment = fragment;
      this._langPref = langPref;
      this._langEntriesByCodeUnique = CreateCodeToEntryMapping(fragment, supportedLocales);

      ConfigureLanguageList();
    }

    private static Dictionary<string, LanguageEntry> CreateCodeToEntryMapping(PreferenceFragmentCompat fragment, HashSet<string> supportedLocales)
    {
      var localesByCode = new Dictionary<string, List<Java.Util.Locale>>();
      foreach (var loc in Java.Util.Locale.GetAvailableLocales())
      {
        if (!supportedLocales.Contains(loc.Language))
          continue;
        if (!localesByCode.ContainsKey(loc.Language))
        {
          localesByCode[loc.Language] = new List<Java.Util.Locale>();
        }
        localesByCode[loc.Language].Add(loc);
      }

      var langEntriesByCodeUnique = localesByCode
          .Select(l => new KeyValuePair<string, LanguageEntry>(l.Key, LanguageEntry.OfLocale(l.Value.First())))
          .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

      var sysLangEntry = LanguageEntry.SystemDefault(fragment.GetString(Resource.String.SystemLanguage));
      langEntriesByCodeUnique.Add(sysLangEntry.Code, sysLangEntry);

      return langEntriesByCodeUnique;
    }

    private void ConfigureLanguageList()
    {
      List<KeyValuePair<string, LanguageEntry>> langEntriesList = _langEntriesByCodeUnique
          .OrderByDescending(kvp => kvp.Value.IsSystem)
          .ThenBy(kvp => kvp.Value.Name)
          .ToList();

      _langPref.SetEntries(langEntriesList
          .Select(kvp => kvp.Value.Name)
          .ToArray());
      _langPref.SetEntryValues(langEntriesList
          .Select(kvp => kvp.Value.Code)
          .ToArray());

      _langPref.Summary = GetDisplayLanguage(LanguageEntry.PrefCodeToLanguage(_langPref.Value));
      _langPref.PreferenceChange += AppLanguagePrefChange;
    }

    private string GetDisplayLanguage(string languageCode)
    {
      if (languageCode != null && this._langEntriesByCodeUnique.ContainsKey(languageCode))
        return this._langEntriesByCodeUnique[languageCode]?.Name;
      else
        return _fragment.GetString(Resource.String.SystemLanguage);
    }

    private void AppLanguagePrefChange(object sender, AndroidX.Preference.Preference.PreferenceChangeEventArgs args)
    {
      string langCode = LanguageEntry.PrefCodeToLanguage((string)args.NewValue);
      LocaleManager.Language = langCode;
      _langPref.Summary = GetDisplayLanguage(langCode);
    }
  }

#pragma warning restore CS0618 // Type or member is obsolete


  /// <summary>
  /// Activity to configure the application and database settings. The database must be unlocked, and this activity will close if it becomes locked.
  /// </summary>
  [Activity(Label = "@string/app_name", Theme = "@style/Kp2aTheme_ActionBar", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden)]
  public class DatabaseSettingsActivity : LockCloseActivity, PreferenceFragmentCompat.IOnPreferenceStartFragmentCallback
  {

    public static void Launch(Activity ctx)
    {
      ctx.StartActivity(new Intent(ctx, typeof(DatabaseSettingsActivity)));
    }

    private ActivityDesign _design;

    public DatabaseSettingsActivity()
    {
      _design = new ActivityDesign(this);
      settingsFragmentManager = new SettingsFragmentManager(this);
    }

    protected override void OnCreate(Bundle savedInstanceState)
    {
      _design.ApplyTheme();
      base.OnCreate(savedInstanceState);
      new Util.InsetListener(FindViewById(Resource.Id.settings)).Apply();


    }

    public SettingsFragmentManager settingsFragmentManager;
    public bool OnPreferenceStartFragment(PreferenceFragmentCompat caller, Preference pref)
    {
      return settingsFragmentManager.OnPreferenceStartFragment(caller, pref);
    }

  }
}

